package com.yunju.xuexin.xuexincrawler.biz;

import com.alibaba.fastjson.JSON;
import com.yunju.xuexin.xuexincrawler.Exception.LoginFailedException;
import com.yunju.xuexin.xuexincrawler.Exception.NeedVerifyCodeExcepiton;
import com.yunju.xuexin.xuexincrawler.Model.ProxyModel;
import com.yunju.xuexin.xuexincrawler.Model.RestVo;
import com.yunju.xuexin.xuexincrawler.Model.XuexEntity;
import com.yunju.xuexin.xuexincrawler.utils.HtmlUtils;
import okhttp3.*;
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 学信网查询服务
 *
 * @author liuzhijun
 */
@Service
@Scope("prototype")
public class XuexinService {

    private Map<String, String> headerMap = new HashMap<String, String>();
    private HashMap<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>();
    private List<ProxyModel> proxyModels = null;
    private String userName;
    private String userPwd;
    private Document verifyLoginPage;
    private OkHttpClient client;
    private String sessionId = UUID.randomUUID().toString();

    String result = null;
    SSLContext sslctxt;

    public XuexinService() {}

    /**
     * 登录学信网
     *
     * @return
     */
    public RestVo login(String userName, String userPwd) {

        this.userName = userName;
        this.userPwd = userPwd;


        //添加代理池
//        proxyModels = OkhttpManager.freshProxy();
        proxyModels = new ArrayList<>();
        ProxyModel proxyModel = new ProxyModel();
        proxyModel.setHost("117.95.7.34");
        proxyModel.setPort(2671);

        ProxyModel proxyModel2 = new ProxyModel();
        proxyModel2.setHost("36.33.20.241");
        proxyModel2.setPort(6436);
        proxyModels.add(proxyModel2);
        proxyModels.add(proxyModel);
        //如果返回为空，则调用自己的IP代理，并发送代理失败通知
        System.out.println(JSON.toJSONString(proxyModels));

        //初始化客户端
        Resource resource = new ClassPathResource("xuexinwang.cer");
        try {
            OkhttpManager okhttpManager = OkhttpManager.getInstance();
            okhttpManager.setTrustrCertificates(
                    resource.getInputStream());
            String loginProxy = JSON.toJSONString(proxyModels.get(0));
            System.out.println(loginProxy);
            client = okhttpManager.build(cookieStore, proxyModels.get(0));
        } catch (IOException e1) {
            e1.printStackTrace();
            return RestVo.build().setResCode(-1)
                    .setMsg("初始化客户端失败,请重试" + e1.getMessage());
        }

        try {
            //访问登录页，打开登录页，获取jsseionid和登陆地址,以及登陆表单

            headerMap.put("Connection","close");
            Request request = new Request.Builder()
                    .headers(Headers.of(headerMap))
                    .url("https://account.chsi.com.cn/passport/login?service=https%3A%2F%2Fmy.chsi.com.cn%2Farchive%2Fj_spring_cas_security_check")
                    .build();
            Response response = client.newCall(request).execute();
            if (!response.isSuccessful())
                throw new IOException("Unexpected code " + response.code());
            org.jsoup.nodes.Document doc = Jsoup
                    .parse(response.body().string());
            //获取登陆地址  input.action
            String actionSubmit = doc.getElementById("fm1").attr("action").toString();
            System.out.println(actionSubmit);
            //登陆login
            String loginCookieStr = doLogin(doc, actionSubmit, null);

            //登陆成功后，访问个人学籍页面获取学籍信息
            // 学籍信息页面：https://my.chsi.com.cn/archive/gdjy/xj/show.action
            return getStudentInfo(loginCookieStr);
        } catch (SocketTimeoutException ste) {
            ste.printStackTrace();
            return RestVo.build().setMsg(ste.getMessage()).setResCode(-1).setSessionId(sessionId);
        } catch (IOException e) {
            e.printStackTrace();
            return RestVo.build().setMsg(e.getMessage()).setResCode(-1).setSessionId(sessionId);
        } catch (NeedVerifyCodeExcepiton needVerifyCodeExcepiton) {
            String codeBase64 = null;
            try {
                codeBase64 = freshCode();

            } catch (IOException e) {
                e.printStackTrace();
            }

            return RestVo.build().setMsg(needVerifyCodeExcepiton.getMessage()).setResCode(1).setObj(codeBase64).setVerifyCode(true).setSessionId(sessionId);
        } catch (LoginFailedException loginFailedException) {
            return RestVo.build().setMsg(loginFailedException.getMessage()).setResCode(-1).setSessionId(sessionId);
        }

    }

    private RestVo getStudentInfo(String loginCookieStr) throws IOException {
        String infoUrl = "https://my.chsi.com.cn/archive/gdjy/xj/show.action";
        headerMap.put("Cookie", loginCookieStr);
        headerMap.put("Host", "my.chsi.com.cn");
        Request infoRequest = new Request.Builder()
                .headers(Headers.of(headerMap)).url(infoUrl).build();
        Response infoRes = client.newCall(infoRequest).execute();
        String infoBody = infoRes.body().string();
        //根据学籍页面返回的内容，再次判断是否登陆成功
        org.jsoup.nodes.Document logindoc = Jsoup.parse(infoBody);
        if (logindoc.select("div.xj-m-r") == null || logindoc.select("div.xj-m-r").size() == 0) {
            System.out.println("登陆失败或者页面变化!!!");
            System.out.println(infoBody);
            FileUtils.writeStringToFile(new File("D:/error.log"), infoBody);
            return RestVo.build().setMsg("采集失败!").setResCode(-1);
        }
        XuexEntity entity = parseBody(logindoc);
        return RestVo.build().setObj(entity).setResCode(0).setSessionId(sessionId);
    }

    /**
     * 登陆学信网，获取login cookie
     *
     * @param document     登陆页面
     * @param actionSubmit 登陆forminput 的action地址
     * @return login cookie
     * @throws IOException
     * @throws NeedVerifyCodeExcepiton
     * @throws LoginFailedException
     */
    public String doLogin(Document document, String actionSubmit, String captcha) throws IOException, NeedVerifyCodeExcepiton, LoginFailedException {
        Elements form = document.select("form#fm1");// 获取登录表单并且填写账号密码
        form.select("#username").val(userName);
        form.select("#password").val(userPwd);

        if (form.select("#captcha").size() > 0) {
            form.select("#captcha").val(captcha);
        }
        Map<String, String> formData = HtmlUtils.formData(form);
        // cookieStr+
        headerMap.clear();
        headerMap
                .put("Referer",
                        "https://account.chsi.com.cn/passport/login?service=https%3A%2F%2Fmy.chsi.com.cn%2Farchive%2Fj_spring_cas_security_check");
        headerMap.put("Upgrade-Insecure-Request", "1");
        headerMap
                .put("User-Agent",
                        "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36");

        HttpUrl url = HttpUrl.parse("https://account.chsi.com.cn" + actionSubmit);
        List<Cookie> cookies = cookieStore.get(url.host());
        if (cookies != null && cookies.size() > 0) {
            String cookieStr = cookieHeader(cookies);
            headerMap.put("Cookie", cookieStr);
        }

        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        for (String key : formData.keySet()) {
            formBodyBuilder.add(key, formData.get(key));
        }
        FormBody formBody = formBodyBuilder.build();
        Request loginRequest = new Request.Builder()
                .headers(Headers.of(headerMap))
                .url("https://account.chsi.com.cn" + actionSubmit)
                .post(formBody).build();
        Response loginResponse = client.newCall(loginRequest).execute();

//          https://account.chsi.com.cn/passport/captcha.image?id=3000.0286099176665
//            验证登陆验证码
        Document prologin = Jsoup.parse(loginResponse.body().string());
        FileUtils.writeStringToFile(new File("D:/prologin.html"), prologin.html());
        if (prologin.getElementById("captcha") != null) {
            verifyLoginPage = prologin;
            throw new NeedVerifyCodeExcepiton("需要验证码!");
        }

        List<String> loginSetCookies = loginResponse.headers("Set-Cookie");
        String loginCookieStr = "";
        for (String string : loginSetCookies) {
            loginCookieStr += string + ";";
        }
        System.out.println("loginCookieStr is : " + loginCookieStr);
//        if (StringUtils.isEmpty(loginCookieStr)) {
//            throw new LoginFailedException("登录失败!");
//        }

        return loginCookieStr;
    }


    public String freshCode() throws IOException {
        headerMap.clear();
        double randomD = Math.random() * 10000;
        HttpUrl url = HttpUrl.parse("https://account.chsi.com.cn/passport/captcha.image?id=" + randomD);
        List<Cookie> cookies = cookieStore.get(url.host());
        if (cookies != null && cookies.size() > 0) {
            String cookieStr = cookieHeader(cookies);
            headerMap.put("Cookie", cookieStr);
        }
//        Accept:image/webp,image/apng,image/*,*/*;q=0.8
//        Accept-Encoding:gzip, deflate, br
//        Accept-Language:zh-CN,zh;q=0.8
//        Connection:keep-alive
        headerMap
                .put("Referer",
                        "https://account.chsi.com.cn/passport/login?service=https%3A%2F%2Fmy.chsi.com.cn%2Farchive%2Fj_spring_cas_security_check");
        headerMap.put("Upgrade-Insecure-Request", "1");
        headerMap
                .put("User-Agent",
                        "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36");

        Request captchaRequest = new Request.Builder()
                .headers(Headers.of(headerMap))
                .url("https://account.chsi.com.cn/passport/captcha.image?id=" + randomD).build();
        Response captchaResponse = client.newCall(captchaRequest).execute();
        byte[] codePicInput = captchaResponse.body().bytes();
        BASE64Encoder encoder = new BASE64Encoder();
        // 返回Base64编码过的字节数组字符串
        String codeString = encoder.encode(codePicInput);
        System.out.println("verifycode code detail is : " + codeString);
        return codeString;
    }

    private String cookieHeader(List<Cookie> cookies) {
        StringBuilder cookieHeader = new StringBuilder();
        for (int i = 0, size = cookies.size(); i < size; i++) {
            if (i > 0) {
                cookieHeader.append("; ");
            }
            Cookie cookie = cookies.get(i);
            cookieHeader.append(cookie.name()).append('=').append(cookie.value());
        }
        return cookieHeader.toString();
    }

    /**
     * 解析学信网数据
     *
     * @param loginBody
     * @throws Exception
     */
    private XuexEntity parseBody(Document loginBody) {
        XuexEntity xuexin = new XuexEntity();

        org.jsoup.nodes.Document doc = loginBody;


        Element infoDiv = doc.select("div.xj-m-r").get(0);
        Element table = infoDiv.select("table.mb-table").get(0);
        Elements trs = table.getElementsByTag("tr");
        Element trEle = trs.get(0);
        Elements tds = trEle.getElementsByTag("td");
        String name = tds.get(0).text();
        String sex = tds.get(1).text();

        trEle = trs.get(1);
        tds = trEle.getElementsByTag("td");
        String birthday = tds.get(0).text();
        String minzu = tds.get(1).text();

        trEle = trs.get(2);
        tds = trEle.getElementsByTag("td");
        String idCard = tds.get(0).text();
        String xuexiaoname = tds.get(1).text();

        trEle = trs.get(3);
        tds = trEle.getElementsByTag("td");
        String xueli = tds.get(0).text();
        String zhuanye = tds.get(1).text();

        trEle = trs.get(4);
        tds = trEle.getElementsByTag("td");
        String xuezhi = tds.get(0).text();
        String xuelileibie = tds.get(1).text();

        trEle = trs.get(5);
        tds = trEle.getElementsByTag("td");
        String xuexixingshi = tds.get(0).text();
        String fenyuan = tds.get(1).text();

        trEle = trs.get(6);
        tds = trEle.getElementsByTag("td");
        String xi = tds.get(0).text();
        String banji = tds.get(1).text();

        trEle = trs.get(7);
        tds = trEle.getElementsByTag("td");
        String xuehao = tds.get(0).text();
        String ruxueriqi = tds.get(1).text();

        trEle = trs.get(8);
        tds = trEle.getElementsByTag("td");
        String lixiaoriqi = tds.get(0).text();
        String xuejizhuangtai = tds.get(1).text();

        xuexin.setBanji(banji);
        xuexin.setBirthDay(birthday);
        xuexin.setFenyuan(fenyuan);
        xuexin.setIdCard(idCard);
        xuexin.setLixiaoriqi(lixiaoriqi);
        xuexin.setMinzu(minzu);
        xuexin.setName(name);
        xuexin.setRuxueriqi(ruxueriqi);
        xuexin.setSchoolName(xuexiaoname);
        xuexin.setSex(sex);
        xuexin.setXi(xi);
        xuexin.setXuehao(xuehao);
        xuexin.setXuejizhuangtai(xuejizhuangtai);
        xuexin.setXuelileibie(xuelileibie);
        xuexin.setXuewei(xueli);
        xuexin.setXuexixingshi(xuexixingshi);
        xuexin.setXuezhi(xuezhi);
        xuexin.setZhuanye(zhuanye);
        String regx = "initDataInfo.+";
        Pattern pattern = Pattern.compile(regx);
        Matcher matcher = pattern.matcher(loginBody.html());
        while (matcher.find()) {
            String line = matcher.group();
            System.out.println(line);
            // , "华中农业大学"
            // initDataInfo("ds2-238973086"+"-m1", "华中农业大学");
            // initDataInfo("ds2-238973086"+"-m2", "信息管理与信息系统");
            // initDataInfo("ds2-238973086"+"-m4", "061020029");
            // initDataInfo("ds2-238973086"+"-m5", "本科");
            // initDataInfo("ds2-238973086"+"-m6", "普通");
            // initDataInfo("ds2-238973086"+"-m7", "普通全日制");
            // initDataInfo("ds2-238973086"+"-m8", "371321198709244512");
            String value = line.split(",")[1].split("\\)")[0].replaceAll(
                    "\"", "");
            System.out.println(value);
            if (line.contains("m1")) {
                xuexin.setSchoolName(value);
            }
            if (line.contains("m2")) {
                xuexin.setZhuanye(value);
            }
            if (line.contains("m8")) {
                xuexin.setIdCard(value);
            }
            if (line.contains("m4")) {
                xuexin.setXuehao(value);
            }
            if (line.contains("m5")) {
                xuexin.setXuewei(value);
            }
            if (line.contains("m6")) {
                xuexin.setXuelileibie(value);
            }
            if (line.contains("m7")) {
                xuexin.setXuexixingshi(value);
            }

        }
        System.out.println(JSON.toJSONString(xuexin));
        return xuexin;


    }


    /**
     * 验证码登陆
     *
     * @return
     */
    public RestVo reLogin(String userName, String passwd, String captcha) {

        //获取登陆地址  input.action
        String actionSubmit = verifyLoginPage.getElementById("fm1").attr("action").toString();
        System.out.println(actionSubmit);
        this.userName = userName;
        this.userPwd = passwd;
        try {
            return getStudentInfo(doLogin(verifyLoginPage, actionSubmit, captcha));
        } catch (IOException e) {
            return RestVo.build().setMsg(e.getMessage()).setResCode(-1);
        } catch (NeedVerifyCodeExcepiton needVerifyCodeExcepiton) {
            String codeBase64 = null;
            try {
                codeBase64 = freshCode();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return RestVo.build().setMsg("验证码错误").setResCode(1).setObj(codeBase64).setVerifyCode(true).setSessionId(sessionId);
        } catch (LoginFailedException el) {
            return RestVo.build().setMsg(el.getMessage()).setResCode(-1);
        }

    }

    public static void main(String[] args) {
        XuexinService xxService = new XuexinService();
        xxService.login("371321198709244512", "liuzhijun.211");
    }
}
